package cn.dengta.webapp.trade.service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import cn.dengta.common.model.Status;
import cn.dengta.context.model.RedisKey;
import cn.dengta.context.model.WesKit;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.trade.dao.MetaDao;
import cn.dengta.webapp.trade.entity.Meta;
import cn.dengta.webapp.user.model.MetaType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import me.codeplayer.util.*;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;

import static cn.dengta.webapp.trade.entity.Meta.*;

/**
 * 元数据表 服务实现类
 *
 * @author lgy
 * @since 2022-08-17
 */
@DubboService
public class MetaService extends BaseServiceImpl<Meta, MetaDao, Long> implements RemoteMetaSyncService {

	Supplier<Map<String, List<Meta>>> metaLoader;
	@Resource
	StringRedisTemplate stringRedisTemplate;

	/**
	 * 缓存（一天）
	 */
	protected List<Meta> typeLoader(boolean flush, List<String> types) {
		HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
		final String key = RedisKey.META;
		if (flush || Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {
			List<Meta> configs = find(null);
			if (X.isValid(configs)) {
				Map<String, String> cfgMap = new HashMap<>(X.size(configs));
				for (Map.Entry<String, List<Meta>> entry : configs.stream().collect(Collectors.groupingBy(c -> c.getType().name())).entrySet()) {
					cfgMap.put(entry.getKey(), JSONUtil.encode(entry.getValue()));
				}
				opsForHash.putAll(key, cfgMap);
				stringRedisTemplate.expire(key, EasyDate.MILLIS_OF_DAY, TimeUnit.MILLISECONDS);
			}
		}
		if (!X.isValid(types)) {
			return Collections.emptyList();
		}
		List<String> entries = opsForHash.multiGet(key, types);
		List<Meta> cfgList = null;
		if (X.isValid(entries)) {
			cfgList = new ArrayList<>();
			for (String str : entries) {
				if (StringUtil.notEmpty(str)) {
					cfgList.addAll(JSONUtil.parseArray(str, Meta.class));
				}
			}
		}
		return X.isValid(cfgList) ? cfgList : Collections.emptyList();
	}

	public List<Meta> find(MetaType type) {
		QueryWrapper<Meta> wrapper = new QueryWrapper<>();
		wrapper.eq(type != null, TYPE, type)
				.ge(STATUS, Status.YES.value);
		wrapper.orderByAsc(SORT, ID);
		return selectList(wrapper);
	}

	public List<Meta> findCacheByType(MetaType type) {
		return findCacheByType(false, type);
	}

	public Meta findSingleCacheByType(MetaType type) {
		List<Meta> metaList = findCacheByType(false, type);
		if (X.isValid(metaList)) {
			return metaList.get(0);
		}
		return null;
	}

	public Meta findSingleCacheByTypeAndWeskit(MetaType type, WesKit wesKit) {
		List<Meta> metaList = findCacheByType(false, type);
		if (X.isValid(metaList)) {
			if (wesKit != null) {
				for (Meta meta : metaList) {
					if (wesKit.name().equals(meta.getLabel())) {
						return meta;
					}
				}
			}
			return metaList.get(0);
		}
		return null;
	}

	/**
	 * 根据类型返回Map<值, Meta>
	 */
	public Map<String, Meta> findMapCacheByType(MetaType type) {
		List<Meta> metaList = findCacheByType(false, type);
		if (X.isValid(metaList)) {
			return CollectionUtil.toHashMap(metaList, Meta::getLabel);
		}
		return null;
	}

	/**
	 * 根据类型返回Map<值, Integer>
	 */
	public Map<String, Integer> findValueMapCacheByType(MetaType type, Integer defaultValue) {
		List<Meta> metaList = findCacheByType(false, type);
		if (X.isValid(metaList)) {
			return CollectionUtil.toHashMap(metaList, Meta::getLabel, c -> NumberUtil.getInteger(c.getValue(), defaultValue));
		}
		return null;
	}

	public List<Meta> findCacheByType(boolean flush, MetaType type) {
		if (type == null) {
			return Collections.emptyList();
		}
		final String name = type.name();
		/*
		final Map<String, List<Meta>> loadMetaMap = loadMeta(flush);
		List<Meta> metas = loadMetaMap.get(name);
		if (!X.isValid(metas)) {
		*/
		List<Meta> metas = typeLoader(flush, List.of(name));
		if (!X.isValid(metas)) {
			List<Meta> configs = find(type);
			if (X.isValid(configs)) {
				stringRedisTemplate.opsForHash().put(RedisKey.META, name, JSONUtil.encode(configs));
				stringRedisTemplate.expire(RedisKey.META, EasyDate.MILLIS_OF_DAY, TimeUnit.MILLISECONDS);
				metas = configs;
			}
		}
		// loadMetaMap.put(name, metas); }
		return metas;
	}

	public Map<String, List<Meta>> loadMeta(boolean flush) {
		Supplier<Map<String, List<Meta>>> loader = metaLoader;
		if (flush || loader == null) {
			metaLoader = loader = new TimeBasedCacheLoader<>(EasyDate.MILLIS_OF_HOUR * 2, ConcurrentHashMap::new);
		}
		return loader.get();
	}

	/**
	 * 根据筛选条件获取数据
	 */
	public List<Meta> find(String label, MetaType type) {
		QueryWrapper<Meta> wrapper = popWrapper(label, type);
		wrapper.ge(STATUS, Status.YES.value);
		wrapper.orderByAsc(SORT);
		return selectList(wrapper);
	}

	public QueryWrapper<Meta> popWrapper(String label, MetaType type) {
		QueryWrapper<Meta> wrapper = new QueryWrapper<>();
		wrapper.eq(StringUtil.notEmpty(label), LABEL, label)
				.eq(type != null, TYPE, type);
		return wrapper;
	}

	@Override
	public void flushCache(String key, Object obj) {
		final boolean flush = obj == null;
		Map<String, List<Meta>> loadMeta = loadMeta(flush);
		if (!flush) {
			loadMeta.put(key, X.castType(obj));
		}
	}

}
